package Queue_and_Stack;

/*
完全平方数
给你一个整数 n ，返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数，其值等于另一个整数的平方；换句话说，其值等于一个整数自乘的积。例如，1、4、9 和 16 都是完全平方数，而 3 和 11 不是。
示例1：
输入：n = 12
输出：3
解释：12 = 4 + 4 + 4
示例 2：
输入：n = 13
输出：2
解释：13 = 4 + 9
作者：LeetCode
链接：https://leetcode.cn/leetbook/read/queue-stack/kfgtt/
 */

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

public class _23完全平方数 {
    public static void main(String[] args) {

        System.out.println(numSquares(2));

    }

    //BFS
    public static int numSquares(int n) {
        int len = (int) Math.sqrt(n);
        int step = 1;
        Queue<Integer> queue = new LinkedList<>();
        //通过set去除重复数加快效率
        Set<Integer> set = new HashSet<>();
        for (int i = 1; i <= len; i++) {
            if (i * i == n) {
                return step;
            }
            queue.offer(i * i);
            set.add(i * i);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            step++;
            for (int i = 0; i < size; i++) {
                int temp = queue.poll();
                for (int j = 1; j <= len; j++) {
                    int sum = temp + j * j;
                    if (sum < n) {
                        if (!set.contains(sum)) {
                            queue.offer(sum);
                            set.add(sum);
                        }
                    } else if (sum == n) {
                        return step;
                    }else {
                        break;
                    }
                }
            }
        }
        return -1;
    }

    //官解：方法一：DP
    /*
    作者：力扣官方题解
    链接：https://leetcode.cn/problems/perfect-squares/solutions/822940/wan-quan-ping-fang-shu-by-leetcode-solut-t99c/
     */
    class Solution {
        public int numSquares(int n) {
            int[] f = new int[n + 1];
            for (int i = 1; i <= n; i++) {
                int minn = Integer.MAX_VALUE;
                for (int j = 1; j * j <= i; j++) {
                    minn = Math.min(minn, f[i - j * j]);
                }
                f[i] = minn + 1;
            }
            return f[n];
        }
    }

}
